Linguaggio C

 

per collaborazioni, commenti, critiche, e altro contattateci alla e-mail: clubinfo@libero.it risponderemo al più presto!

Introduzione dell'autore

di Luca Sabatucci

Lezione 1

Pagina principale | Lezione successiva


Composizione del corso

Il linguaggio C è un linguaggio duttile, completo, estremamente potente che permette di ottenere risultati superabili solo dalla programmazione diretta in linguaggio macchina.
Ma questo non vuol dire che il C vada bene per ogni utente e per realizzare qualunque tipo di programma!
La sua potenza, infatti, equivale alla quasi totale mancanza di limiti. Sebbene fino a poco tempo fa non la pensassi così, il linguaggio C non è adatto ai neofiti.
Per cominciare a programmare serve un linguaggio che inquadri il programmatore entro degli schemi ben definiti, come ad esempio fa il Pascal o anche il Java.
Il C questo non lo fa! Chi programma in C ha il completo controllo della macchina ed è libero di creare il programma più bello del mondo o di danneggiarsi irreparabilmente il contenuto della memoria del proprio computer. Ad esempio, niente vieta al programmatore di creare un programma che scriva in modo casuale nella memoria del computer, provocando inevitabilmente un crash di sistema.
Ma non bisogna scoraggiarsi! E' proprio questa estrema potenza che ci permetterà di usare inizialmente il C come se fosse un linguaggio vincolato, e solo quando saremo esperti programmatori che ci libereremo dalle catene scopriremo la programmazione più avanzata

Struttura del corso

Questo corso è dedicato a tre categorie di lettori:

  1. neofiti, ovvero lettori che si avvicinano in assoluto adesso alla programmazione
  2. nuovi acquisti, programmatori che hanno già programmato ma non in linguaggio C
  3. esperti, programmatori che hanno conoscono già la sintassi del linguaggio C, ma vogliono approfondire le strutture e la programmazione avanzata.

Questa è una breve introduzione al corso che analizza i motivi per i quali si dovrebbe imparare questo linguaggio, rispetto ad altri. Per questo è consigliata la lettura a tutti.

Panoramica del linguaggio

Il C nasce nel 1974 nei laboratori della Bell Labs da D. Richie come evoluzione del BCPL. Lo scopo con il quale è nato (che spiega immediatamente le sue caratteristiche sopra accennate) è stato da subito quello di realizzare un sistema operativo indipendente quanto più possibile dall' hardware: lo UNIX.

Il C favorisce la programmazione strutturata

Affinché un programma sia considerato valido deve essere soggetto ad alcuni requisiti

  1. correttezza: il programma deve fare ciò che effettivamente si ci aspetti che faccia. Non deve quindi presentare comportamenti non previsti o errati.
  2. leggibilità: il listato deve essere auto esplicativo; chiunque lo legga deve avere almeno una idea su che operazioni svolge
  3. efficienza: il programma deve effettuare le operazioni utilizzando meno risorse possibili
  4. modularità: il programma deve essere strutturato in modo tale che possa essere suddiviso in blocchi quanto più indipendenti l'uno dall'altro
  5. modificabilità: deve essere possibile effettuare delle modifiche in un punto qualunque del programma senza che queste lo compromettano completamente
  6. parametricità: il programma (o le sue parti) si deve mantenere quanto più generico possibile in modo che sia possibile il riutilizzo per altri scopi
  7. portabilità: il listato deve essere tale da essere compilato in ambienti e sistemi operativi diversi
  8. robustezza: significa limitare i comportamenti imprevisti in casi in cui l'ambiente in cui gira il programma ha delle anomalie.

Naturalmente queste caratteristiche non dipendono esclusivamente dal linguaggio, ma soprattutto dal programmatore. Il C permette di ottenere tutto ciò tramite una peculiarità molto importante (ormai largamente diffusa, ma da non considerare dovuta): supporta la programmazione strutturata. Questo significa che il programma può essere strutturato secondo il meccanismo dell'astrazione in moduli, indipendenti l'uno dall'altro. I meccanismi fondamentali per la strutturazione sono:

  1. la struttura di sequenza, che permette di comporre istruzioni ed eseguirle una di seguito all'altra
  2. la struttura condizionale, che permette di eseguire una tra due o più istruzioni, a seconda del valore di una espressione booleana (strutture if, if ... else, if ... else if e switch ... case)
  3. la struttura di iterazione, che permette di eseguire ripetutamente una istruzione, sotto il controllo di una espressione booleana (strutture while, do ... while e for)

Il successo di tali strutture è quello che ognuna di esse ha uno solo punto di ingresso e un solo punto di uscita. Questa proprietà le rende semplici da comprendere e da usare.
Analizzeremo ognuna di esse in seguito cercando di chiarire con degli esempi

Il C è un linguaggio a tipi

Il ruolo del tipo assume una grande importanza con il linguaggio C. Quando si crea una variabile è necessario indicare a priori che tipo di valori questa potrà assumere. Questo permette di fare dei controlli sulla correttezza del programma stesso. Assegnamenti eterogenei tra tipi non sono infatti permessi, e vengono segnalati dal compilatore, in modo che l'utente si possa accorgere dell'errore.

I tipi del C si dividono in tipi primitivi e tipi definiti dall'utente. I primi sono:

rispettivamente per i numeri interi, i caratteri, i numeri a virgola mobile e i numeri a doppia precisione. Si può notare l'assenza di due tipi molto usati in altri linguaggi: booleani e stringhe. I primi non esistono e possono essere sostituiti dagli interi. Le stringhe invece possono essere usate con un piccolo artificio che vedremo, ma restano comunque più complesse da gestire rispetto ad altri linguaggi a più alto livello.
I tipi composti invece si definiscono mediante la parola chiave typedef.
Altri tipi, ingiustamente poco usati, sono quelli enumerativi definiti dalla parola chiave enum.

Librerie di funzioni

Una delle caratteristiche che ha reso il C un linguaggio di successo è che ogni modulo può essere compilato indipendentemente dagli altri, per creare dei file oggetto (infatti hanno estensione .OBJ) che contengono già il codice eseguibile. L'operazione di creare un unico eseguibile a partire dai file oggetto si chiama Link e rende il processo di generazione del codice molto veloce perché in fase di debug non è necessario rigenerare l'intero codice per ogni minima modifica. Questo influenza positivamente i tempi di programmazione e incoraggia a separare quanto più possibili parti del codice che sono "logicamente" indipendenti.

Inoltre vengono solitamente fornite insieme al compilatore un insieme di funzioni (già precompilate) chiamate librerie. Le librerie permettono di utilizzare pezzi di codice già scritti (e collaudati) da altri, i quali velocizzano il processo e non costringono il programmatore a reinventare ogni volta la ruota.
Lo stesso programmatore può crearsi librerie di funzioni che usa più spesso risparmiando di volta in volta lo sforzo di dover trovare la funzione cercata tra i propri file.
Si può paragonare l'uso delle librerie di funzioni al gioco delle costruzioni. Componendo i pezzi più semplici (già esistenti) si possono creare composizioni sempre nuove e molto più grandi.

Pregi e difetti dei puntatori

Un puntatore è una variabile che contiene un indirizzo di memoria. Questo consente di accedere alla locazione di memoria puntata qualunque essa sia (potrebbe essere una sezione dati o una parte di codice del sistema operativo, o addirittura dello stesso programma in esecuzione).
Inutile dire che i puntatori sono molto importanti per creare strutture dati altrimenti non implementabili in nessuna altra maniera (strutture dati ricorsive), ma se usati impropriamente sono un'arma a doppio taglio.
Un puntatore che punta ad una locazione di memoria non voluta arreca danni irreparabili e non prevedibili a priori. Si tratta degli errori più difficili da scovare e da correggere

Quasi tutti i linguaggi ricorrono ai puntatori, ma spesso non fanno accorgere l'utente del loro uso. Un esempio per tutti il Java. Nel Java è impossibile lavorare senza utilizzare i puntatori, eppure nessun manuale sul Java riporta notizie in merito. Infatti nel Java i puntatori vengono chiamati riferimenti e non è possibile fare alcun tipo di operazione su tali variabili. L'unica operazione concessa è l'assegnamento: quando viene creato un riferimento questo ha il valore NULL. Un riferimento può assumere valore NULL oppure può essere assegnato ad una variabile temporanea. Poiché la vita della variabile temporanea è legata a quella del riferimento non sussiste (quasi) alcun problema.

Il linguaggio C, tanto per smentirsi, permette di trattare i puntatori come variabili normali. Si possono incrementare, decrementare, sommare ad un intero o tra di loro, ecc... Questo genere di operazioni che fornisce grande libertà al programmatore va però tenuta sotto controllo con un uso quanto più limitato possibile.
Tanto per fare un esempio della potenza dei puntatori in C, è possibile creare un puntatore a qualsiasi tipo di variabile, compreso ad un puntatore: possono quindi essere dichiarati puntatori di puntatori e puntatori di puntatori di puntatori... In realtà se i puntatori giocano un ruolo molto importante, i puntatori di puntatori sono usati poco spesso e i puntatori di puntatori di puntatori praticamente mai perché rendono incomprensibile il listato, andando contro il principio di leggibilità.


Bibliografia


Testi consigliati per l'apprendimento

Questo articolo è stato scaricato dal Club di informatica
Pagina curata da Luca Sabatucci